const fs = require('fs')
const path = require('path')
const parse = require('@babel/parser')
const traverse = require('@babel/traverse').default
const babel = require('@babel/core')
const getModuleInfo = (file) => {
    const body = fs.readFileSync(file, 'utf-8')
    //生成AST抽象语法树
    const ast = parse.parse(body, {
        sourceType: 'module'//告诉babel我们的代码时esmodule模块化代码
    })
    const deps = {}
    //遍历AST抽象语法树，收集依赖
    traverse(ast, {
        //声明需要找到的节点类型为ImportDeclaration
        ImportDeclaration({ node }) {
            const dirname = path.dirname(file)//获取当前正在分析的文件所在目录路径
            //比如当前正在分析的文件是./src/index.js-----dirname就是./src
            const absPath = './' + path.join(dirname, node.source.value)//计算依赖模块的相对路径
            //node.source.value为import后面的路径字符串，比如import './add.js'
            //node.source.value为./add.js
            deps[node.source.value] = absPath
        }
    })
    //根据AST生成低版本js代码
    const { code } = babel.transformFromAst(ast, null, {//ast为抽象语法树，null源代码字符串，这里设为null表示直接使用AST
        presets: ['@babel/preset-env']
    })
    const moduleInfo = {
        file,
        deps,
        code
    }
    return moduleInfo
}
const parseModules = (file) => {
    const entry = getModuleInfo(file)
    //递归分析入口文件的依赖
    const temp = [entry]
    for (let i = 0; i < temp.length; i++) {
        const deps = temp[i].deps
        if (deps) {
            for (let key in deps) {
                if (deps.hasOwnProperty(key)) {
                    temp.push(getModuleInfo(deps[key]))
                }
            }
        }
    }
    const depsGraph = {}
    for (let i = 0; i < temp.length; i++) {
        const file = temp[i].file
        const deps = temp[i].deps
        const code = temp[i].code
        depsGraph[file] = {
            deps,
            code
        }
    }
    //将分析好的模块信息返回
    return depsGraph
}
//处理require函数和exports对象
const bundle = (file) => {
    const depsGraph = JSON.stringify(parseModules(file))
    return `(function(graph) {
            function require(file) {
              function absRequire(relPath) {
                return require(graph[file].deps[relPath])
              }
              var exports = {};
              (function(require, exports, code) {
                eval(code)
              })(absRequire, exports, graph[file].code);
              return exports
            }
            require("${file}")
          })(${depsGraph});`
}

const content = bundle('./src/index.js')
fs.mkdirSync('./dist')
fs.writeFileSync('./dist/bundle.js', content)



